
#include "graphics/material.h"
#include "graphics/pass.h"

namespace dw
{
namespace graphics
{
Material::Material(const std::string& name) : 
	_name(name),
	_ambient(Colorf::White), _diffuse(Colorf::White), _specular(Colorf::White),
	_shininess(0),
	_ka(0), _kd(0), _ks(0), 
	_kr(0), _kt(0),
	_ior(0)
{
}

Material::Material(const Colorf& a, const Colorf& d, const Colorf& s) :
	_ambient(a), _diffuse(d), _specular(s),
	_shininess(0),
	_ka(0), _kd(0), _ks(0), 
	_kr(0), _kt(0),
	_ior(0)
{
}

Material::~Material()
{
	for (unsigned i = 0; i < _passes.size(); ++i)
		delete _passes[i];
}

Material& Material::operator=(const Material& rhs)
{
	_ambient		= rhs._ambient;
	_diffuse		= rhs._diffuse; 
	_specular		= rhs._specular;

	_shininess	= rhs._shininess;
	
	_ka		= rhs._ka; 
	_kd		= rhs._kd;
	_ks		= rhs._ks;
	_kr		= rhs._kr;
	_kt		= rhs._kt;
	_ior	= rhs._ior;
	
	return *this;
}

Pass* Material::createPass()
{
	Pass* pass = new Pass;
	_passes.push_back(pass);
	return pass;
}

void Material::removePass(const unsigned i)
{
	PassIterator passIter = _passes.begin() + i;
	delete *passIter;
	_passes.erase(passIter);
}

void Material::removePass(Pass* pass)
{
	PassIterator passIter = std::find(_passes.begin(), _passes.end(), pass);
	delete *passIter;
	_passes.erase(passIter);
}

void Material::addPass(Pass* pass)
{
	_passes.push_back(pass);
}

bool Material::isTransparent() const
{
	return _opacity < 1.f;
}

uint32 Material::begin() const
{
	Colorf specular = _ks * _specular;

	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, &_ambient.r);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, &_diffuse.r);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r);
	glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, &_emissive.r);
	glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, _shininess * 100);
	
	return static_cast< uint32 >(_passes.size());
}

void Material::end() const
{
	static float ambientDefault[] = { 0.2f, 0.2f, 0.2f, 1.0f };
	static float diffuseDefault[] = { 0.8f, 0.8f, 0.8f, 1.0f };
	static float specularDefault[] = { 0.0f, 0.0f, 0.0f, 1.0f };
	static float emissionDefault[] = { 0.0f, 0.0f, 0.0f, 1.0f };
	static float shininessDefault = 0;

	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambientDefault);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuseDefault);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specularDefault);
	glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emissionDefault);
	glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininessDefault);
}

void Material::beginPass(const unsigned i) const
{
	if (i < _passes.size())
		_passes[i]->begin();
}

void Material::endPass(const unsigned i) const
{
	if (i < _passes.size())
		_passes[i]->end();
}

std::string Material::getSignature() const
{
	return "dw::graphics::Material";
}

void Material::serialize(ISerializer* serializer) const
{
	//serializer->serialize(_name);
	//serializer->serialize(_ambient);
	//serializer->serialize(_diffuse);
	//serializer->serialize(_specular);
	//serializer->serialize(_emissive);
	//serializer->serialize(_ka);
	//serializer->serialize(_kd);
	//serializer->serialize(_ks);
	//serializer->serialize(_shininess);
	//serializer->serialize(_passes);
}

void Material::deserialize(ISerializer* serializer)
{
	//serializer->deserialize(_name);
	//serializer->deserialize(_ambient);
	//serializer->deserialize(_diffuse);
	//serializer->deserialize(_specular);
	//serializer->deserialize(_emissive);
	//serializer->deserialize(_ka);
	//serializer->deserialize(_kd);
	//serializer->deserialize(_ks);
	//serializer->deserialize(_shininess);
	//serializer->deserialize(_passes);
}

std::ostream& operator<<(std::ostream& os, const Material& material)
{ 
	unsigned nameLength = static_cast< unsigned >(material._name.size());
	unsigned numPasses	= static_cast< unsigned >(material._passes.size());

	os.write(reinterpret_cast< const char* >(&nameLength), sizeof (unsigned));
	os.write(material._name.c_str(), nameLength + 1);
	os.write(reinterpret_cast< const char* >(&material._ambient), sizeof (Colorf));
	os.write(reinterpret_cast< const char* >(&material._diffuse), sizeof (Colorf));
	os.write(reinterpret_cast< const char* >(&material._specular), sizeof (Colorf));
	os.write(reinterpret_cast< const char* >(&material._emissive), sizeof (Colorf));
	os.write(reinterpret_cast< const char* >(&material._ka), sizeof (float));
	os.write(reinterpret_cast< const char* >(&material._kd), sizeof (float));
	os.write(reinterpret_cast< const char* >(&material._ks), sizeof (float));
	os.write(reinterpret_cast< const char* >(&material._shininess), sizeof (float));
	os.write(reinterpret_cast< const char* >(&material._opacity), sizeof (float));
	os.write(reinterpret_cast< const char* >(&numPasses), sizeof (unsigned));
	for (uint32 i = 0; i < numPasses; ++i)
		os << *material._passes[i];
	return os;
}

std::istream& operator>>(std::istream& is, Material& material)
{
	char* name;
	unsigned nameLength;
	unsigned numPasses;

	is.read(reinterpret_cast< char* >(&nameLength), sizeof (unsigned));
	name = new char[nameLength + 1];
	is.read(name, nameLength + 1);
	material._name.assign(name, nameLength);
	delete [] name;
	is.read(reinterpret_cast< char* >(&material._ambient), sizeof (Colorf));
	is.read(reinterpret_cast< char* >(&material._diffuse), sizeof (Colorf));
	is.read(reinterpret_cast< char* >(&material._specular), sizeof (Colorf));
	is.read(reinterpret_cast< char* >(&material._emissive), sizeof (Colorf));
	is.read(reinterpret_cast< char* >(&material._ka), sizeof (float));
	is.read(reinterpret_cast< char* >(&material._kd), sizeof (float));
	is.read(reinterpret_cast< char* >(&material._ks), sizeof (float));
	is.read(reinterpret_cast< char* >(&material._shininess), sizeof (float));
	is.read(reinterpret_cast< char* >(&material._opacity), sizeof (float));
	is.read(reinterpret_cast< char* >(&numPasses), sizeof (unsigned));
	for (uint32 i = 0; i < numPasses; ++i)
	{
		Pass* pass = material.createPass();
		is >> *pass;
	}
	return is;
}

} // namespace graphics
} // namespace dw